Don Traber update Garmin Training Center to add read. Writes V2 format now.
authorrobertl <robertl>
Fri, 12 Sep 2008 15:18:08 +0000 (15:18 +0000)
committerrobertl <robertl>
Fri, 12 Sep 2008 15:18:08 +0000 (15:18 +0000)
gtrnctr.c
internal_styles.c
xmldoc/formats/gtrnctr.xml

index 296f6bef254e41b212dabf9c7f649e125e99656a..e09e20d110e7197e302111dee49588c90c06c8b5 100644 (file)
--- a/gtrnctr.c
+++ b/gtrnctr.c
 static gbfile *ofd;
 static waypoint *wpt_tmp;
 static route_head *trk_head;
+static computed_trkdata *tdata;
 
 #define MYNAME "gtc"
 
+#define GTC_MAX_NAME_LEN 15
+
+#define MAX_SPORTS 4
+static char gtc_sportlist[MAX_SPORTS][16] = { "Biking", "Running", "MultiSport", "Other" };
+static int gtc_sport = 0;
+static int gtc_course_flag;
+
+static time_t gtc_least_time;
+static time_t gtc_most_time;
+static double gtc_start_lat;
+static double gtc_start_long;
+static double gtc_end_lat;
+static double gtc_end_long;
+
+static char *opt_sport, *opt_course;
+
 static
 arglist_t gtc_args[] = {
+       { "course", &opt_course, "Write course rather than history, default yes",
+         "1", ARGTYPE_BOOL, ARG_NOMINMAX},
+       { "sport", &opt_sport, "Sport: Biking (deflt), Running, MultiSport, Other", 
+         "Biking", ARGTYPE_STRING, ARG_NOMINMAX},
        ARG_TERMINATOR
 };
-#if 0
+
+#if ! HAVE_LIBEXPAT
+static void
+gtc_rd_init(const char *fname)
+{
+       fatal(MYNAME ": this format does not support reading.\n");
+}
+
+static void
+gtc_read(void)
+{
+}
+#else
+
 /* Tracks */
-static xg_callback     gl_trk_s;
-// static xg_callback  gl_trk_ident;
-static xg_callback     gl_trk_pnt_s, gl_trk_pnt_e;
-static xg_callback     gl_trk_utc;
-static xg_callback     gl_trk_lat;
-static xg_callback     gl_trk_long;
-static xg_callback     gl_trk_alt;
-
-static xg_tag_mapping gl_map[] = {
- { gl_trk_s,    cb_start, "/History/Run/Track" },
- { gl_trk_pnt_s,cb_start, "/History/Run/Track/Trackpoint/Position" },
- { gl_trk_pnt_e,cb_end,   "/History/Run/Track/Trackpoint/Position" },
- { gl_trk_lat,  cb_cdata, "/History/Run/Track/Trackpoint/Position/Latitude" },
- { gl_trk_long, cb_cdata, "/History/Run/Track/Trackpoint/Position/Longitude" },
- { gl_trk_alt,  cb_cdata, "/History/Run/Track/Trackpoint/Position/Altitude" },
- { gl_trk_utc,  cb_cdata, "/History/Run/Track/Trackpoint/Time" },
- { NULL,       0,         NULL}
+static xg_callback     gtc_trk_s;
+static xg_callback     gtc_trk_ident;
+static xg_callback     gtc_trk_pnt_s, gtc_trk_pnt_e;
+static xg_callback     gtc_trk_utc;
+static xg_callback     gtc_trk_lat;
+static xg_callback     gtc_trk_long;
+static xg_callback     gtc_trk_alt;
+static xg_callback     gtc_trk_hr;
+static xg_callback     gtc_trk_cad;
+
+static xg_tag_mapping gtc_map[] = {
+       /* courses tcx v1 & v2 */
+       { gtc_trk_s,    cb_start, "/Courses/Course" },
+       { gtc_trk_ident,cb_cdata, "/Courses/Course/Name"},
+       { gtc_trk_pnt_s,cb_start, "/Courses/Course/Track/Trackpoint" },
+       { gtc_trk_pnt_e,cb_end,   "/Courses/Course/Track/Trackpoint" },
+       { gtc_trk_utc,  cb_cdata, "/Courses/Course/Track/Trackpoint/Time" },
+       { gtc_trk_lat,  cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LatitudeDegrees" },
+       { gtc_trk_long, cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LongitudeDegrees" },
+       { gtc_trk_alt,  cb_cdata, "/Courses/Course/Track/Trackpoint/AltitudeMeters" },
+       { gtc_trk_alt,  cb_cdata, "/Courses/Course/Track/Trackpoint/AltitudeMeters" },
+       { gtc_trk_hr,   cb_cdata, "/Courses/Course/Track/Trackpoint/HeartRateBpm" },
+       { gtc_trk_cad,  cb_cdata, "/Courses/Course/Track/Trackpoint/Cadence" },
+
+       /* history tcx v2 (activities) */
+       { gtc_trk_s,    cb_start, "/Activities/Activity" },
+       { gtc_trk_ident,cb_cdata, "/Activities/Activity/Id" },
+       { gtc_trk_pnt_s,cb_start, "/Activities/Activity/Track/Trackpoint" },
+       { gtc_trk_pnt_e,cb_end,   "/Activities/Activity/Track/Trackpoint" },
+       { gtc_trk_utc,  cb_cdata, "/Activities/Activity/Track/Trackpoint/Time" },
+       { gtc_trk_lat,  cb_cdata, "/Activities/Activity/Track/Trackpoint/Position/LatitudeDegrees" },
+       { gtc_trk_long, cb_cdata, "/Activities/Activity/Track/Trackpoint/Position/LongitudeDegrees" },
+       { gtc_trk_alt,  cb_cdata, "/Activities/Activity/Track/Trackpoint/AltitudeMeters" },
+       { gtc_trk_hr,   cb_cdata, "/Activities/Activity/Track/Trackpoint/HeartRateBpm" },
+       { gtc_trk_cad,  cb_cdata, "/Activities/Activity/Track/Trackpoint/Cadence" },
+
+       /* history tcx v1 */
+       { gtc_trk_s,    cb_start, "/History/Run" },
+       { gtc_trk_ident,cb_cdata, "/History/Run/Id" },
+       { gtc_trk_pnt_s,cb_start, "/History/Run/Track/Trackpoint" },
+       { gtc_trk_pnt_e,cb_end,   "/History/Run/Track/Trackpoint" },
+       { gtc_trk_utc,  cb_cdata, "/History/Run/Track/Trackpoint/Time" },
+       { gtc_trk_lat,  cb_cdata, "/History/Run/Track/Trackpoint/Position/LatitudeDegrees" },
+       { gtc_trk_long, cb_cdata, "/History/Run/Track/Trackpoint/Position/LongitudeDegrees" },
+       { gtc_trk_alt,  cb_cdata, "/History/Run/Track/Trackpoint/AltitudeMeters" },
+       { gtc_trk_hr,   cb_cdata, "/History/Run/Track/Trackpoint/HeartRateBpm" },
+       { gtc_trk_cad,  cb_cdata, "/History/Run/Track/Trackpoint/Cadence" },
+
+       { NULL,         0,         NULL}
+};
+
+static const char * 
+gtc_tags_to_ignore[] = {
+        "TrainingCenterDatabase",
+       "Lap",
+       "CourseFolder",
+       "Running",
+       "Biking",
+       "Other",
+       "Multisport",
+       NULL,
 };
-#endif
 
 static void
 gtc_rd_init(const char *fname)
 {
-       fatal(MYNAME ": this format does not support reading.\n");
+       xml_init(fname, gtc_map, NULL);
+       xml_ignore_tags(gtc_tags_to_ignore);
 }
 
-#if 0
 static void
 gtc_read(void)
 {
@@ -77,16 +154,30 @@ gtc_rd_deinit(void)
 static void
 gtc_wr_init(const char *fname)
 {
+        int i;
+
         ofd = gbfopen(fname, "w", MYNAME);
+
+       if(opt_sport) {
+               for(i = 0; i < MAX_SPORTS; i++) {
+                       if(0 == case_ignore_strncmp(opt_sport, gtc_sportlist[i], 2)) {
+                               gtc_sport = i;
+                               break;
+                       }
+               }
+       }
+       gtc_course_flag = atoi(opt_course);
 }
 
 static void
 gtc_wr_deinit(void)
 {
         gbfclose(ofd);
+       xfree(tdata);
 }
 
 static int gtc_indent_level;
+
 static void 
 gtc_write_xml(int indent, const char *fmt, ...)
 {
@@ -107,32 +198,19 @@ gtc_write_xml(int indent, const char *fmt, ...)
 static void
 gtc_waypt_pr(const waypoint *wpt)
 {      
-#if 0
-       gbfprintf(ofd, "            <Trackpoint>\n");
-       gbfprintf(ofd, "                <Position>\n");
-       gbfprintf(ofd, "                    <Latitude>%.5f</Latitude>\n", wpt->latitude);
-       gbfprintf(ofd, "                    <Longitude>%.5f</Longitude>\n", wpt->longitude);
-       if (wpt->altitude != unknown_alt) {
-               gbfprintf(ofd, "                    <Altitude>%.3f</Altitude>\n", wpt->altitude);
+       if (wpt->wpt_flags.is_split != 0) {
+               gtc_write_xml(1, "<Trackpoint split=\"yes\">\n");
+       } else {
+               gtc_write_xml(1, "<Trackpoint>\n");
        }
-       gbfprintf(ofd, "                </Position>\n");
-       gbfprintf(ofd, "                ");
-       xml_write_time(ofd, wpt->creation_time, "Time");
-       gbfprintf(ofd, "            </Trackpoint>\n");
-#else
-    if (wpt->wpt_flags.is_split != 0) {
-        gtc_write_xml(1, "<Trackpoint split=\"yes\">\n");
-    } else {
-        gtc_write_xml(1, "<Trackpoint>\n");
-    }
 
        if (wpt->creation_time) {
                char time_string[100];
                xml_fill_in_time(time_string, wpt->creation_time, wpt->microseconds,
-                       XML_LONG_TIME);
+                                XML_LONG_TIME);
                if (time_string[0]) {
                        gtc_write_xml(0, "<Time>%s</Time>\n", 
-                               time_string);
+                                     time_string);
                }
        }
        gtc_write_xml(1, "<Position>\n");
@@ -143,31 +221,116 @@ gtc_waypt_pr(const waypoint *wpt)
                gtc_write_xml(0, "<AltitudeMeters>%f</AltitudeMeters>\n", wpt->altitude);
        }
        if (wpt->heartrate) {
-               gtc_write_xml(0, "<HeartRateBpm>%d</HeartRateBpm>\n", wpt->heartrate);
+               //gtc_write_xml(0, "<HeartRateBpm>%d</HeartRateBpm>\n", wpt->heartrate);
+               gtc_write_xml(1, "<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+               gtc_write_xml(0,"<Value>%d</Value>\n", wpt->heartrate);
+               gtc_write_xml(-1,"</HeartRateBpm>\n");
        }
-       if (wpt->cadence) {
+       if (wpt->cadence) {
                gtc_write_xml(0, "<Cadence>%d</Cadence>\n", wpt->cadence);
        }
 
-
        gtc_write_xml(-1, "</Trackpoint>\n");
-#endif
 }
 
 static void
-gtc_hdr( const route_head *rte)
+gtc_fake_hdr(void)
+{
+       long secs = 0;
+       if (gtc_least_time && gtc_most_time) {
+               secs = gtc_most_time - gtc_least_time;
+       }
+       if(gtc_course_flag) { /* course format */
+
+               gtc_write_xml(0, "<TotalTimeSeconds>%d</TotalTimeSeconds>\n", secs);
+               gtc_write_xml(0, "<DistanceMeters>%lf</DistanceMeters>\n", 
+                             tdata->distance_meters ? tdata->distance_meters : 0);
+               gtc_write_xml(1, "<BeginPosition>\n");
+               gtc_write_xml(0, "<LatitudeDegrees>%lf</LatitudeDegrees>\n", gtc_start_lat);
+               gtc_write_xml(0, "<LongitudeDegrees>%lf</LongitudeDegrees>\n", gtc_start_long);
+               gtc_write_xml(-1, "</BeginPosition>\n");
+               gtc_write_xml(1, "<EndPosition>\n");
+               gtc_write_xml(0, "<LatitudeDegrees>%lf</LatitudeDegrees>\n", gtc_end_lat);
+               gtc_write_xml(0, "<LongitudeDegrees>%lf</LongitudeDegrees>\n", gtc_end_long);
+               gtc_write_xml(-1, "</EndPosition>\n");
+               gtc_write_xml(1, "<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+               gtc_write_xml(0,"<Value>%g</Value>\n",
+                             tdata->avg_hrt ? tdata->avg_hrt : 100);
+               gtc_write_xml(-1, "</AverageHeartRateBpm>\n");
+               gtc_write_xml(1, "<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+               gtc_write_xml(0,"<Value>%d</Value>\n",
+                             tdata->max_hrt ? tdata->max_hrt : 200);
+               gtc_write_xml(-1,"</MaximumHeartRateBpm>\n");
+               gtc_write_xml(0, "<Intensity>Active</Intensity>\n");
+
+       } else {  /* activity (history) format */
+
+               gtc_write_xml(0, "<TotalTimeSeconds>%d</TotalTimeSeconds>\n", secs);
+               gtc_write_xml(0, "<DistanceMeters>%lf</DistanceMeters>\n",
+                             tdata->distance_meters ? tdata->distance_meters : 1000);
+               gtc_write_xml(0, "<MaximumSpeed>0</MaximumSpeed>\n");
+               gtc_write_xml(0, "<Calories>0</Calories>\n");
+               gtc_write_xml(1, "<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+               gtc_write_xml(0,"<Value>%g</Value>\n",
+                             tdata->avg_hrt ? tdata->avg_hrt : 100);
+               gtc_write_xml(-1, "</AverageHeartRateBpm>\n");
+               gtc_write_xml(1, "<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+               gtc_write_xml(0,"<Value>%d</Value>\n",
+                             tdata->max_hrt ? tdata->max_hrt : 200);
+               gtc_write_xml(-1,"</MaximumHeartRateBpm>\n");
+               gtc_write_xml(0, "<Intensity>Active</Intensity>\n");
+               gtc_write_xml(0, "<TriggerMethod>Manual</TriggerMethod>\n");
+       }
+}
+
+static void
+gtc_act_hdr( const route_head *rte)
 {
+       gtc_write_xml(1, "<Activity Sport=\"%s\">\n", gtc_sportlist[gtc_sport]);
+       if (gtc_least_time) {
+               char time_string[100];
+               xml_fill_in_time(time_string, gtc_least_time, 0, XML_LONG_TIME);
+               gtc_write_xml(0, "<Id>%s</Id>\n", time_string);
+               gtc_write_xml(1, "<Lap StartTime=\"%s\">\n", time_string);
+       } else {
+               gtc_write_xml(1, "<Lap>\n");
+       }
+       gtc_fake_hdr();
        gtc_write_xml(1,"<Track>\n");
 }
 
 static void
-gtc_ftr(const route_head *rte)
+gtc_act_ftr(const route_head *rte)
 {
-       gtc_write_xml(-1,"</Track>\n");
+       gtc_write_xml(-1, "</Track>\n");
+       gtc_write_xml(-1, "</Lap>\n");
+       gtc_write_xml(-1, "</Activity>\n");
 }
 
-static time_t gtc_least_time;
-static time_t gtc_most_time;
+static void
+gtc_crs_hdr( const route_head *rte)
+{
+
+        gtc_write_xml(1, "<Course>\n");
+        if(rte->rte_name) {
+               gtc_write_xml(0, "<Name>%s</Name>\n", xstrndup(rte->rte_name, GTC_MAX_NAME_LEN));
+       } else {
+               gtc_write_xml(0, "<Name>New Course</Name>\n");
+       }
+       /* write_optional_xml_entity(ofd, "      ", "Name", rte->rte_name); */
+       gtc_write_xml(1, "<Lap>\n");
+       gtc_fake_hdr();
+       gtc_write_xml(-1, "</Lap>\n");
+       gtc_write_xml(1,"<Track>\n");
+}
+
+static void
+gtc_crs_ftr(const route_head *rte)
+{
+       gtc_write_xml(-1,"</Track>\n");
+       gtc_write_xml(-1, "</Course>\n");
+
+}
 
 static void
 gtc_lap_start(const route_head *rte)
@@ -177,131 +340,130 @@ gtc_lap_start(const route_head *rte)
 }
 
 static void
-gtc_study_lap(const waypoint *wpt)
+gtc_new_study_lap(const route_head *rte)
 {
-       if (wpt->creation_time && (gtc_least_time == 0)) 
-               gtc_least_time = wpt->creation_time;
-
-       if (wpt->creation_time && (gtc_least_time > wpt->creation_time))
-               gtc_least_time =  wpt->creation_time;
-
-       if (wpt->creation_time > gtc_most_time) 
-               gtc_most_time = wpt->creation_time;
+       track_recompute(rte, &tdata);  /* called routine allocates space for tdata */
 }
 
 static void
-gtc_fake_hdr(void)
+gtc_study_lap(const waypoint *wpt)
 {
-       long secs = 0;
-       if (gtc_least_time && gtc_most_time) {
-               secs = gtc_most_time - gtc_least_time;
+       if (wpt->creation_time && (gtc_least_time == 0)) {
+               gtc_least_time = wpt->creation_time;
+               gtc_start_lat = wpt->latitude;
+               gtc_start_long = wpt->longitude;
+       }
+  
+       if (wpt->creation_time && (gtc_least_time > wpt->creation_time)) {
+               gtc_least_time =  wpt->creation_time;
+               gtc_start_lat = wpt->latitude;
+               gtc_start_long = wpt->longitude;
+       }
+       if (wpt->creation_time > gtc_most_time)  {
+               gtc_most_time = wpt->creation_time;
+               gtc_end_lat = wpt->latitude;
+               gtc_end_long = wpt->longitude;
        }
-       gtc_write_xml(0, "<TotalTimeSeconds>%d</TotalTimeSeconds>\n", secs);
-       gtc_write_xml(0, "<DistanceMeters>0</DistanceMeters>\n");
-       gtc_write_xml(0, "<Calories>0</Calories>\n");
-       gtc_write_xml(0, "<AverageHeartRateBpm>0</AverageHeartRateBpm>\n");
-       gtc_write_xml(0, "<MaximumHeartRateBpm>0</MaximumHeartRateBpm>\n");
-       gtc_write_xml(0, "<Intensity>Active</Intensity>\n");
-       gtc_write_xml(0, "<TriggerMethod>Manual</TriggerMethod>\n");
 }
 
 void
 gtc_write(void)
 {
-#if 0
-       gbfprintf(ofd, "<?xml version=\"1.0\" ?>\n");
-       gbfprintf(ofd, "<History xmlns=\"http://www.garmin.com/xmlschemas/ForerunnerLogbook\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/ForerunnerLogbook http://www.garmin.com/xmlschemas/ForerunnerLogbookv1.xsd\" version=\"1\">\n");
-       gbfprintf(ofd, "    <Run>\n");
-       track_disp_all(gtc_hdr, gtc_ftr, gtc_waypt_pr);
-       gbfprintf(ofd, "    </Run>\n");
-       gbfprintf(ofd, "</History>\n");
-#else
        gtc_write_xml(0, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
-       gtc_write_xml(1, "<TrainingCenterDatabase\nxmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v1\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v1\nhttp://www.garmin.com/xmlschemas/TrainingCenterDatabasev1.xsd\">\n");
-       gtc_write_xml(1, "<History>\n");
-
-       gtc_write_xml(1, "<Running>\n");
-       gtc_write_xml(1, "<Run>\n");
+       gtc_write_xml(1, "<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n");
 
        gtc_lap_start(NULL);
        track_disp_all(NULL, NULL, gtc_study_lap);
+       track_disp_all(gtc_new_study_lap, NULL, NULL);
 
-       if (gtc_least_time) {
-               char time_string[100];
-               xml_fill_in_time(time_string, gtc_least_time, 0, XML_LONG_TIME);
-               gtc_write_xml(1, "<Lap StartTime=\"%s\">\n", time_string);
+       if(gtc_course_flag) {
+               gtc_write_xml(1, "<Courses>\n");
+               track_disp_all(gtc_crs_hdr, gtc_crs_ftr, gtc_waypt_pr);
+               gtc_write_xml(-1, "</Courses>\n");
        } else {
-               gtc_write_xml(1, "<Lap>\n");
+               gtc_write_xml(1, "<Activities>\n");
+               track_disp_all(gtc_act_hdr, gtc_act_ftr, gtc_waypt_pr);
+               gtc_write_xml(-1, "</Activities>\n");
        }
-       gtc_fake_hdr();
-       track_disp_all(gtc_hdr, gtc_ftr, gtc_waypt_pr);
-       gtc_write_xml(-1, "</Lap>\n");
-       gtc_write_xml(-1, "</Run>\n");
-       gtc_write_xml(-1, "</Running>\n");
-       gtc_write_xml(0, "<Biking />\n");
-       gtc_write_xml(0, "<Other />\n");
-       gtc_write_xml(0, "<MultiSport />\n");
 
-       gtc_write_xml(-1, "</History>\n");
        gtc_write_xml(-1, "</TrainingCenterDatabase>\n");
-
-#endif
 }
 
-void   gl_trk_s(const char *args, const char **unused)
+void
+gtc_trk_s(const char *unused, const char **attrv)
 {
        trk_head = route_head_alloc();
        track_add_head(trk_head);
 }
-#if 0
-void   gl_trk_ident(const char *args, const char **unused)
+
+void
+gtc_trk_ident(const char *args, const char **unused)
 {
        trk_head->rte_name = xstrdup(args);
 }
-#endif
 
-void   gl_trk_pnt_s(const char *args, const char **unused)
+void
+gtc_trk_pnt_s(const char *unused, const char **attrv)
 {
        wpt_tmp = waypt_new();
 }
 
-void   gl_trk_pnt_e(const char *args, const char **unused)
+void
+gtc_trk_pnt_e(const char *args, const char **unused)
 {
-       track_add_wpt(trk_head, wpt_tmp);
+        if(wpt_tmp->longitude != 0. && wpt_tmp->latitude != 0.) track_add_wpt(trk_head, wpt_tmp);
 }
 
-void   gl_trk_utc(const char *args, const char **unused)
+void
+gtc_trk_utc(const char *args, const char **unused)
 {
        wpt_tmp->creation_time = xml_parse_time(args, NULL);
 }
 
-void   gl_trk_lat(const char *args, const char **unused)
+void
+gtc_trk_lat(const char *args, const char **unused)
 {
        wpt_tmp->latitude = atof(args);
 }
 
-void   gl_trk_long(const char *args, const char **unused)
+void
+gtc_trk_long(const char *args, const char **unused)
 {
        wpt_tmp->longitude = atof(args);
 }
 
-void   gl_trk_alt(const char *args, const char **unused)
+void
+gtc_trk_alt(const char *args, const char **unused)
 {
        wpt_tmp->altitude = atof(args);
 }
 
+void
+gtc_trk_hr(const char *args, const char **unused)
+{
+       wpt_tmp->heartrate = atoi(args);
+}
 
+void
+gtc_trk_cad(const char *args, const char **unused)
+{
+       wpt_tmp->cadence = atoi(args);
+}
 
 ff_vecs_t gtc_vecs = {
         ff_type_file,
-       { ff_cap_none, ff_cap_write, ff_cap_none},
+       { 
+               ff_cap_none                     /* waypoints */, 
+               ff_cap_read | ff_cap_write      /* tracks */, 
+               ff_cap_none                     /* routes */
+       },
         gtc_rd_init,
         gtc_wr_init,
-        NULL,
+        gtc_rd_deinit,
         gtc_wr_deinit,
-        NULL,
+        gtc_read,
         gtc_write,
-        NULL,
+       NULL,
         gtc_args,
        CET_CHARSET_ASCII, 0    /* CET-REVIEW */
 };
index 32578b4450f7e2d266458784458408c29fefa8cf..3b053937247a5e926fb42527761aa56bb8ecd7ac 100644 (file)
@@ -512,6 +512,41 @@ static char iblue747[] =
 "IFIELD PATH_SPEED_KPH,\"\",\"%.1f\"           # SPEED\n"
 "IFIELD PATH_DISTANCE_KM,\"\",\"%f\"           # DISTANCE\n"
 ;
+static char igo2008_poi[] = 
+"# gpsbabel XCSV style file\n"
+"#\n"
+"# Format: iGO2008 points of interest\n"
+"# Author: Olaf Klein\n"
+"#   Date: 09/05/2008\n"
+"#\n"
+"DESCRIPTION           iGO2008 points of interest (.upoi)\n"
+"EXTENSION             upoi\n"
+"DATATYPE              WAYPOINT\n"
+"#\n"
+"# FILE LAYOUT DEFINITIIONS:\n"
+"#\n"
+"FIELD_DELIMITER               PIPE\n"
+"RECORD_DELIMITER      CRNEWLINE\n"
+"BADCHARS              \"|\n"
+"ENCODING              MS-ANSI\n"
+"#\n"
+"# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:\n"
+"#\n"
+"IFIELD        INDEX,\"1\",\"%d\"\n"
+"IFIELD        DESCRIPTION, \"\", \"%s\"\n"
+"IFIELD        SHORTNAME, \"\", \"%s\"\n"
+"IFIELD        IGNORE, \"\", \"%s\"            # nothing\n"
+"IFIELD        LAT_DECIMAL, \"\", \"%.6f\"\n"
+"IFIELD        LON_DECIMAL, \"\", \"%.6f\"\n"
+"IFIELD        IGNORE, \"\", \"%s\"            # Name of map\n"
+"IFIELD        IGNORE, \"\", \"%s\"            # nothing\n"
+"IFIELD        POSTAL_CODE, \"\", \"%s\"\n"
+"IFIELD        CITY, \"\", \"%s\"\n"
+"IFIELD        STREET_ADDR, \"\", \"%s\"               # Street without number\n"
+"IFIELD        IGNORE, \"\", \"%s\"            # Street number\n"
+"IFIELD        NOTES, \"\", \"%s\"\n"
+"IFIELD        PHONE_NR, \"\", \"%s\"\n"
+;
 static char kompass_tk[] = 
 "# gpsbabel XCSV style file\n"
 "#\n"
@@ -1180,8 +1215,8 @@ static char xmapwpt[] =
 "IFIELD        IGNORE, \"\", \"%-.31s\"\n"
 "IFIELD        DESCRIPTION, \"\", \"%-.78s\"\n"
 ;
-style_vecs_t style_list[] = {{ "xmapwpt", xmapwpt } , { "xmap2006", xmap2006 } , { "xmap", xmap } , { "tomtom_itn", tomtom_itn } , { "tomtom_asc", tomtom_asc } , { "tabsep", tabsep } , { "sportsim", sportsim } , { "saplus", saplus } , { "s_and_t", s_and_t } , { "openoffice", openoffice } , { "nima", nima } , { "navigonwpt", navigonwpt } , { "mxf", mxf } , { "mapconverter", mapconverter } , { "kwf2", kwf2 } , { "ktf2", ktf2 } , { "kompass_wp", kompass_wp } , { "kompass_tk", kompass_tk } , { "iblue747", iblue747 } , { "gpsman", gpsman } , { "gpsdrivetrack", gpsdrivetrack } , { "gpsdrive", gpsdrive } , { "geonet", geonet } , { "garmin_poi", garmin_poi } , { "garmin301", garmin301 } , { "fugawi", fugawi } , { "dna", dna } , { "custom", custom } , { "cup", cup } , { "csv", csv } , { "cambridge", cambridge } , { "arc", arc } ,  {0,0}};
-size_t nstyles = 32;
+style_vecs_t style_list[] = {{ "xmapwpt", xmapwpt } , { "xmap2006", xmap2006 } , { "xmap", xmap } , { "tomtom_itn", tomtom_itn } , { "tomtom_asc", tomtom_asc } , { "tabsep", tabsep } , { "sportsim", sportsim } , { "saplus", saplus } , { "s_and_t", s_and_t } , { "openoffice", openoffice } , { "nima", nima } , { "navigonwpt", navigonwpt } , { "mxf", mxf } , { "mapconverter", mapconverter } , { "kwf2", kwf2 } , { "ktf2", ktf2 } , { "kompass_wp", kompass_wp } , { "kompass_tk", kompass_tk } , { "igo2008_poi", igo2008_poi } , { "iblue747", iblue747 } , { "gpsman", gpsman } , { "gpsdrivetrack", gpsdrivetrack } , { "gpsdrive", gpsdrive } , { "geonet", geonet } , { "garmin_poi", garmin_poi } , { "garmin301", garmin301 } , { "fugawi", fugawi } , { "dna", dna } , { "custom", custom } , { "cup", cup } , { "csv", csv } , { "cambridge", cambridge } , { "arc", arc } ,  {0,0}};
+size_t nstyles = 33;
 #else /* CSVFMTS_ENABLED */
 style_vecs_t style_list[] = {{0,0}};
 size_t nstyles = 0;
index 34c59528d7c4a915c95bcda217fc979eee0750c2..37f02c9bd224ff2e888d9df3ef84747bc5b96ef1 100644 (file)
@@ -1,16 +1,34 @@
 <para>
-GPSBabel has limited support for Garmin Training Center files.   That program is the successor to Garmin' Logbook program for their workout units.  It is a free upgrade.
+GPSBabel supports reading and writing of tracks in the .tcx 
+format used by Garmin Training Center (GTC). GTC is the successor 
+to Garmin's Logbook program for their workout units.  It is a 
+free upgrade.
 </para>
 <para>
-This format is somewhat underachieving in GPSBabel.  It is a write-only
-format; we never read it.  The bigger problem, however, is a fundamental
-impedance mismatch between this format and most of what we support.  GPSBabel
-fundamentally deals in waypoints, tracks, and routes.  While we do record
-things like heart rate and temperature when we know it, the fundamentals
-of Training Center are different - it deals in concepts like laps and calories
-which are rather alien to GPSBabel and most of the formats we support.  As 
-such, while we can describe the tracks pretty accurately, things like 
-calories and heart zone tracking are not supported.
+GPSBabel can read GTC .tcx v1 and v2 files, and can write v2 files. 
+There is a fundamental mismatch between this format and most of
+what we support.  GPSBabel deals in waypoints, tracks, and routes.
+While we do record things like heart rate and temperature when we know
+it, the fundamentals of Training Center are different.  It deals in
+concepts like laps and calories, which are rather alien to GPSBabel 
+and most of the formats we support.  As such, while we can describe
+the tracks pretty accurately, things like calories and heart zone
+tracking are not supported.
 </para>
+<para>
+One of the most useful things you can do with this format is to send 
+.tcx files found on the web or elsewhere to any supported GPS 
+unit. You will probably want to include the transform (rte=trk) 
+and simplify filters in this process. For example,
+</para>
+<para><userinput>
+gpsbabel -i gtrnctr -f somefile.tcx -x simplify,count=50 
+-x transform,rte=trk -r -o garmin -F usb:
+</userinput></para>
+<para>
+where you select the count not to exceed the number of available
+waypoints for routing on your device.
+</para>
+